/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package argentracker.services.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import argentracker.services.ChppApplication;
import argentracker.services.ChppException;
import argentracker.services.ChppService;

import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;

/**
 * briefly description.
 * 
 * extense description.
 * 
 * @author matero (matero _at_ gmail _dot_ com)
 */
public class AppEngineChppService implements ChppService {

    private static final Logger logger = LoggerFactory.getLogger(AppEngineChppService.class);

    private static final String RECOMMENDED_SERVER = "//HattrickData/RecommendedURL";
    private static final String IS_AUTHENTICATED = "//HattrickData/IsAuthenticated";
    private static final String LOGIN_RESULT = "//HattrickData/LoginResult";

    private final ChppApplication application;

    private String hattrickSite = HATTRICK_SITE;
    private String fileSpec = FILE_SPEC;

    private String serversFile = "servers";
    private double serversVersion = 1.2;

    private String loginFile = "login";
    private double loginVersion = 1.1;

    public AppEngineChppService(ChppApplication chppApplication) {
        application = chppApplication;
    }

    public final void setFileSpec(String newFileSpec) {
        fileSpec = newFileSpec;
    }

    public final void setHattrickSite(String newHattrickSite) {
        hattrickSite = newHattrickSite;
    }

    public final void setServersFile(String newServersFile) {
        serversFile = newServersFile;
    }

    public final void setServersVersion(double newServersVersion) {
        serversVersion = newServersVersion;
    }

    public final void setLoginFile(String newLoginFile) {
        loginFile = newLoginFile;
    }

    public final void setLoginVersion(double newLoginVersion) {
        loginVersion = newLoginVersion;
    }

    @Override
    public String getRecommendedServer() {
        logger.debug("fetching recommended server...");

        ChppRequestBuilder serversRequest = get(serversFile, serversVersion).from(hattrickSite);
        HTTPResponse serversResponse = fetch(serversRequest.create());
        Document servers = getDocumentFrom(serversResponse);
        Node recommendedURL = servers.selectSingleNode(RECOMMENDED_SERVER);

        return recommendedURL.getStringValue();
    }

    @Override
    public String login(String onServer, String loginName, String readonlyPassword) {
        logger.debug("loging into hattrick on server {}, with loginName {}...", onServer, loginName);

        ChppRequestBuilder loginRequest = loginFileFrom(onServer).loginName(loginName).securityCode(readonlyPassword).actionType("login");
        HTTPResponse loginResponse = fetch(loginRequest.create());
        Document login = getDocumentFrom(loginResponse);
        checkAuthentication(login);

        return getSessionCookiesFrom(loginResponse);
    }

    private ChppRequestBuilder loginFileFrom(String server) {
        return get(loginFile, loginVersion).from(server);
    }

    private void checkAuthentication(Document login) {
        if (!isAuthenticated(login)) {
            String loginResultCode = login.selectSingleNode(LOGIN_RESULT).getStringValue();
            throw new ChppException("Login failed. ErrorCode: " + loginResultCode);
        }
    }

    private boolean isAuthenticated(Document login) {
        Node authenticatedNode = login.selectSingleNode(IS_AUTHENTICATED);
        String authenticatedValue = authenticatedNode.getStringValue();
        return Boolean.valueOf(authenticatedValue);
    }

    private String getSessionCookiesFrom(HTTPResponse response) {
        String sessionCookies = "";

        for (HTTPHeader header : response.getHeaders()) {
            if (isSessionCookie(header)) {
                sessionCookies += header.getValue() + ';';
            }
        }

        return sessionCookies;
    }

    private boolean isSessionCookie(HTTPHeader httpHeader) {
        return SESSION_COOKIES_HEADERS.equals(httpHeader.getName());
    }

    private ChppRequestBuilder get(String file, double version) {
        return ChppRequestBuilder.get(file).ofVersion(version).withChppFileSpec(fileSpec).chppApp(application);
    }

    protected Document getDocumentFrom(HTTPResponse serversResponse) {
        try {
            return readDocument(serversResponse.getContent());
        } catch (DocumentException e) {
            String url = serversResponse.getFinalUrl().toString();
            logger.error("Contents fetched from [" + url + "] are illegal.", e);
            throw new ChppException("Contents fetched from [" + url + "] are illegal.", e);
        }
    }

    protected Document readDocument(byte[] contents) throws DocumentException {
        SAXReader xmlReader = new SAXReader();
        return xmlReader.read(new ByteArrayInputStream(contents));
    }

    protected HTTPResponse fetch(HTTPRequest chppRequest) {
        URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
        try {
            return fetchService.fetch(chppRequest);
        } catch (IOException e) {
            logger.error("Hattrick site could notbe contacted to fetch [" + chppRequest.getURL() + "].", e);
            throw new ChppException("Hattrick site could notbe contacted to fetch [" + chppRequest.getURL() + "].", e);
        }
    }

}
